#ifndef GENALG_H
#define GENALG_H

#include <vector>
#include <iostream>
#include <algorithm>

#include "../misc/utils.h"

#include "neuralnet.h"

#define D_MAX_PERTURBATION 0.3
#define I_NUM_COPIES_ELITE 1
#define I_NUM_ELITE		4

using namespace std;

struct Genome
{
	vector<double>	vecWeights;

	double			dFitness;

	Genome():dFitness(0){}

	Genome(vector<double> w, double f): vecWeights(w), dFitness(f){}

	// overload '<' used for sorting
	friend bool operator < (const Genome& lhs, const Genome& rhs)
	{
		return (lhs.dFitness < rhs.dFitness);
	}	
};

class GenAlg
{
private:
	// this holds the entire population of chromesomes
	vector <Genome> m_vecPop;

	// size of population
	int				m_iPopSize;

	// amount of weights per chrome
	int				m_iChromoLength;

	// total fitness of population
	double			m_dTotalFitness;

	// best fitness this population
	double			m_dBestFitness;

	// average fitness
	double			m_dAverageFitness;

	// worst fitness
	double			m_dWorstFitness;

	// keeps track of the best genome
	int				m_iFittestGenome;

	// probability that a chromosomes bits wil mutate
	// try figures around 0.05 to 0.3 ish
	double			m_dMutationRate;

	// probability of chromosomes crossing over bits
	// 0.7 is pretty good
	double			m_dCrossoverRate;

	// generation counter
	int				m_iGeneration;

	void Crossover(	const vector<double>	&mum,
					const vector<double>	&dad,
					vector<double>			&baby1,
					vector<double>			&baby2);

	void Mutate(vector<double> &chromo);

	Genome GetChromoRoulette();

	void GrabBest(	int				iBest,
					const int		iNumCopies,
					vector<Genome> &vecPop);

	void CalculateBestWorstAvTot();

	void Reset();

public:
	GenAlg(	int		iPopSize,
			double	dMutRat,
			double	dCrossRat,
			int		iNumWeights);

	// this runs the GA for one generation
	vector<Genome> Epoch(vector<Genome> &old_pop);

	//accessor methods
	vector<Genome> GetChromos()const{return m_vecPop;}

	double AverageFitness() const{return m_dTotalFitness / m_iPopSize;}

	double BestFitness()const{return m_dBestFitness;}	
};

#endif